﻿[System.CodeDom.Compiler.GeneratedCode("NSwag", "{{ ToolchainVersion }}")]
internal class JsonExceptionConverter : Newtonsoft.Json.JsonConverter
{
    private readonly Newtonsoft.Json.Serialization.DefaultContractResolver _defaultContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver();
    private readonly System.Collections.Generic.IDictionary<string, System.Reflection.Assembly> _searchedNamespaces;
    private readonly bool _hideStackTrace = false;
    
    public JsonExceptionConverter()
    {
        _searchedNamespaces = new System.Collections.Generic.Dictionary<string, System.Reflection.Assembly> { { typeof({{ ExceptionModelClass }}).Namespace, System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof({{ ExceptionModelClass }})).Assembly } };
    }
    
    public override bool CanWrite => true;
    
    public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
    {
        var exception = value as System.Exception;
        if (exception != null)
        {
            var resolver = serializer.ContractResolver as Newtonsoft.Json.Serialization.DefaultContractResolver ?? _defaultContractResolver;
    
            var jObject = new Newtonsoft.Json.Linq.JObject();
            jObject.Add(resolver.GetResolvedPropertyName("discriminator"), exception.GetType().Name);
            jObject.Add(resolver.GetResolvedPropertyName("Message"), exception.Message);
            jObject.Add(resolver.GetResolvedPropertyName("StackTrace"), _hideStackTrace ? "HIDDEN" : exception.StackTrace);
            jObject.Add(resolver.GetResolvedPropertyName("Source"), exception.Source);
            jObject.Add(resolver.GetResolvedPropertyName("InnerException"),
                exception.InnerException != null ? Newtonsoft.Json.Linq.JToken.FromObject(exception.InnerException, serializer) : null);
    
            foreach (var property in GetExceptionProperties(value.GetType()))
            {
                var propertyValue = property.Key.GetValue(exception);
                if (propertyValue != null)
                {
                    jObject.AddFirst(new Newtonsoft.Json.Linq.JProperty(resolver.GetResolvedPropertyName(property.Value),
                        Newtonsoft.Json.Linq.JToken.FromObject(propertyValue, serializer)));
                }
            }
    
            value = jObject;
        }
    
        serializer.Serialize(writer, value);
    }
    
    public override bool CanConvert(System.Type objectType)
    {
        return System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(System.Exception)).IsAssignableFrom(System.Reflection.IntrospectionExtensions.GetTypeInfo(objectType));
    }
    
    public override object ReadJson(Newtonsoft.Json.JsonReader reader, System.Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
    {
        var jObject = serializer.Deserialize<Newtonsoft.Json.Linq.JObject>(reader);
        if (jObject == null)
            return null;
    
        var newSerializer = new Newtonsoft.Json.JsonSerializer();
        newSerializer.ContractResolver = (Newtonsoft.Json.Serialization.IContractResolver)System.Activator.CreateInstance(serializer.ContractResolver.GetType());
    
        var field = GetField(typeof(Newtonsoft.Json.Serialization.DefaultContractResolver), "_sharedCache");
        if (field != null)
            field.SetValue(newSerializer.ContractResolver, false);
    
        dynamic resolver = newSerializer.ContractResolver;
        if (System.Reflection.RuntimeReflectionExtensions.GetRuntimeProperty(newSerializer.ContractResolver.GetType(), "IgnoreSerializableAttribute") != null)
            resolver.IgnoreSerializableAttribute = true;
        if (System.Reflection.RuntimeReflectionExtensions.GetRuntimeProperty(newSerializer.ContractResolver.GetType(), "IgnoreSerializableInterface") != null)
            resolver.IgnoreSerializableInterface = true;
    
        Newtonsoft.Json.Linq.JToken token;
        if (jObject.TryGetValue("discriminator", System.StringComparison.OrdinalIgnoreCase, out token))
        {
            var discriminator = Newtonsoft.Json.Linq.Extensions.Value<string>(token);
            if (objectType.Name.Equals(discriminator) == false)
            {
                var exceptionType = System.Type.GetType("System." + discriminator, false);
                if (exceptionType != null)
                    objectType = exceptionType;
                else
                {
                    foreach (var pair in _searchedNamespaces)
                    {
                        exceptionType = pair.Value.GetType(pair.Key + "." + discriminator);
                        if (exceptionType != null)
                        {
                            objectType = exceptionType;
                            break;
                        }
                    }
    
                }
            }
        }
    
        var value = jObject.ToObject(objectType, newSerializer);
        foreach (var property in GetExceptionProperties(value.GetType()))
        {
            var jValue = jObject.GetValue(resolver.GetResolvedPropertyName(property.Value));
            var propertyValue = (object)jValue?.ToObject(property.Key.PropertyType);
            if (property.Key.SetMethod != null)
                property.Key.SetValue(value, propertyValue);
            else
            {
                field = GetField(objectType, "m_" + property.Value.Substring(0, 1).ToLowerInvariant() + property.Value.Substring(1));
                if (field != null)
                    field.SetValue(value, propertyValue);
            }
        }
    
        SetExceptionFieldValue(jObject, "Message", value, "_message", resolver, newSerializer);
        SetExceptionFieldValue(jObject, "StackTrace", value, "_stackTraceString", resolver, newSerializer);
        SetExceptionFieldValue(jObject, "Source", value, "_source", resolver, newSerializer);
        SetExceptionFieldValue(jObject, "InnerException", value, "_innerException", resolver, serializer);
    
        return value;
    }
    
    private System.Reflection.FieldInfo GetField(System.Type type, string fieldName)
    {
        var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(type).GetDeclaredField(fieldName);
        if (field == null && System.Reflection.IntrospectionExtensions.GetTypeInfo(type).BaseType != null)
            return GetField(System.Reflection.IntrospectionExtensions.GetTypeInfo(type).BaseType, fieldName);
        return field;
    }
    
    private System.Collections.Generic.IDictionary<System.Reflection.PropertyInfo, string> GetExceptionProperties(System.Type exceptionType)
    {
        var result = new System.Collections.Generic.Dictionary<System.Reflection.PropertyInfo, string>();
        foreach (var property in System.Linq.Enumerable.Where(System.Reflection.RuntimeReflectionExtensions.GetRuntimeProperties(exceptionType), 
            p => p.GetMethod?.IsPublic == true))
        {
            var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute<Newtonsoft.Json.JsonPropertyAttribute>(property);
            var propertyName = attribute != null ? attribute.PropertyName : property.Name;
    
            if (!System.Linq.Enumerable.Contains(new[] { "Message", "StackTrace", "Source", "InnerException", "Data", "TargetSite", "HelpLink", "HResult" }, propertyName))
                result[property] = propertyName;
        }
        return result;
    }
    
    private void SetExceptionFieldValue(Newtonsoft.Json.Linq.JObject jObject, string propertyName, object value, string fieldName, Newtonsoft.Json.Serialization.IContractResolver resolver, Newtonsoft.Json.JsonSerializer serializer)
    {
        var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(System.Exception)).GetDeclaredField(fieldName);
        var jsonPropertyName = resolver is Newtonsoft.Json.Serialization.DefaultContractResolver ? ((Newtonsoft.Json.Serialization.DefaultContractResolver)resolver).GetResolvedPropertyName(propertyName) : propertyName;
        var property = System.Linq.Enumerable.FirstOrDefault(jObject.Properties(), p => System.String.Equals(p.Name, jsonPropertyName, System.StringComparison.OrdinalIgnoreCase));
        if (property != null)
        {
            var fieldValue = property.Value.ToObject(field.FieldType, serializer);
            field.SetValue(value, fieldValue);
        }
    }
}